﻿using System;
using Org.BouncyCastle.Crypto.Digests;

namespace Bitcoin.Cryptography
{
    public static class SecurityUtility
    {
        /// <summary>
        /// See <see cref="DoubleDigest(byte[], int, int)"/>.
        /// </summary>
        public static byte[] DoubleDigest(byte[] input)
        {
            return DoubleDigest(input, 0, input.Length);
        }

        /// <summary>
        /// Calculates the SHA-256 hash of the given byte range, and then hashes the resulting hash again. This is
        /// standard procedure in BitCoin. The resulting hash is in big endian form.
        /// </summary>
        public static byte[] DoubleDigest(byte[] input, int offset, int length)
        {
            var sha256 = new Sha256Digest();
            sha256.BlockUpdate(input, offset, length);
            var first = new byte[sha256.GetDigestSize()];
            sha256.DoFinal(first, 0);
            sha256.Reset();
            sha256.BlockUpdate(first, 0, first.Length);
            var output = new byte[sha256.GetDigestSize()];
            sha256.DoFinal(output, 0);
            return output;
        }

        /// <summary>
        /// Calculates SHA256(SHA256(byte range 1 + byte range 2)).
        /// </summary>
        public static byte[] DoubleDigestTwoBuffers(byte[] input1, int offset1, int length1, byte[] input2, int offset2, int length2)
        {
            var buffer = new byte[length1 + length2];
            Array.Copy(input1, offset1, buffer, 0, length1);
            Array.Copy(input2, offset2, buffer, length1, length2);
            return DoubleDigest(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// Calculates RIPEMD160(SHA256(input)). This is used in Address calculations.
        /// </summary>
        public static byte[] Sha256Hash160(byte[] input)
        {
            var sha256 = new Sha256Digest();
            sha256.BlockUpdate(input, 0, input.Length);
            var first = new byte[sha256.GetDigestSize()];
            sha256.DoFinal(first, 0);

            var digest = new RipeMD160Digest();
            digest.BlockUpdate(first, 0, first.Length);
            var output = new byte[20];
            digest.DoFinal(output, 0);
            return output;
        }
    }
}
